package client

import (
	
	
	
	
	
	
	
	
)

// ASExchange performs an AS exchange for the client to retrieve a TGT.
func ( *Client) ( string,  messages.ASReq,  int) (messages.ASRep, error) {
	if ,  := .IsConfigured(); ! {
		return messages.ASRep{}, krberror.Errorf(, krberror.ConfigError, "AS Exchange cannot be performed")
	}

	// Set PAData if required
	 := setPAData(, nil, &)
	if  != nil {
		return messages.ASRep{}, krberror.Errorf(, krberror.KRBMsgError, "AS Exchange Error: issue with setting PAData on AS_REQ")
	}

	,  := .Marshal()
	if  != nil {
		return messages.ASRep{}, krberror.Errorf(, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ")
	}
	var  messages.ASRep

	,  := .sendToKDC(, )
	if  != nil {
		if ,  := .(messages.KRBError);  {
			switch .ErrorCode {
			case errorcode.KDC_ERR_PREAUTH_REQUIRED, errorcode.KDC_ERR_PREAUTH_FAILED:
				// From now on assume this client will need to do this pre-auth and set the PAData
				.settings.assumePreAuthentication = true
				 = setPAData(, &, &)
				if  != nil {
					return messages.ASRep{}, krberror.Errorf(, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required")
				}
				,  := .Marshal()
				if  != nil {
					return messages.ASRep{}, krberror.Errorf(, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData")
				}
				,  = .sendToKDC(, )
				if  != nil {
					if ,  := .(messages.KRBError);  {
						return messages.ASRep{}, krberror.Errorf(, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
					}
					return messages.ASRep{}, krberror.Errorf(, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
				}
			case errorcode.KDC_ERR_WRONG_REALM:
				// Client referral https://tools.ietf.org/html/rfc6806.html#section-7
				if  > 5 {
					return messages.ASRep{}, krberror.Errorf(, krberror.KRBMsgError, "maximum number of client referrals exceeded")
				}
				++
				return .(.CRealm, , )
			default:
				return messages.ASRep{}, krberror.Errorf(, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
			}
		} else {
			return messages.ASRep{}, krberror.Errorf(, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
		}
	}
	 = .Unmarshal()
	if  != nil {
		return messages.ASRep{}, krberror.Errorf(, krberror.EncodingError, "AS Exchange Error: failed to process the AS_REP")
	}
	if ,  := .Verify(.Config, .Credentials, ); ! {
		return messages.ASRep{}, krberror.Errorf(, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid or client password/keytab incorrect")
	}
	return , nil
}

// setPAData adds pre-authentication data to the AS_REQ.
func setPAData( *Client,  *messages.KRBError,  *messages.ASReq) error {
	if !.settings.DisablePAFXFAST() {
		 := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
		.PAData = append(.PAData, )
	}
	if .settings.AssumePreAuthentication() {
		// Identify the etype to use to encrypt the PA Data
		var  etype.EType
		var  error
		var  types.EncryptionKey
		var  int
		if  == nil {
			// This is not in response to an error from the KDC. It is preemptive or renewal
			// There is no KRB Error that tells us the etype to use
			 := .settings.preAuthEType // Use the etype that may have previously been negotiated
			if  == 0 {
				 = int32(.Config.LibDefaults.PreferredPreauthTypes[0]) // Resort to config
			}
			,  = crypto.GetEtype()
			if  != nil {
				return krberror.Errorf(, krberror.EncryptingError, "error getting etype for pre-auth encryption")
			}
			, ,  = .Key(, 0, nil)
			if  != nil {
				return krberror.Errorf(, krberror.EncryptingError, "error getting key from credentials")
			}
		} else {
			// Get the etype to use from the PA data in the KRBError e-data
			,  = preAuthEType()
			if  != nil {
				return krberror.Errorf(, krberror.EncryptingError, "error getting etype for pre-auth encryption")
			}
			.settings.preAuthEType = .GetETypeID() // Set the etype that has been defined for potential future use
			, ,  = .Key(, 0, )
			if  != nil {
				return krberror.Errorf(, krberror.EncryptingError, "error getting key from credentials")
			}
		}
		// Generate the PA data
		,  := types.GetPAEncTSEncAsnMarshalled()
		if  != nil {
			return krberror.Errorf(, krberror.KRBMsgError, "error creating PAEncTSEnc for Pre-Authentication")
		}
		,  := crypto.GetEncryptedData(, , keyusage.AS_REQ_PA_ENC_TIMESTAMP, )
		if  != nil {
			return krberror.Errorf(, krberror.EncryptingError, "error encrypting pre-authentication timestamp")
		}
		,  := .Marshal()
		if  != nil {
			return krberror.Errorf(, krberror.EncodingError, "error marshaling the PAEncTSEnc encrypted data")
		}
		 := types.PAData{
			PADataType:  patype.PA_ENC_TIMESTAMP,
			PADataValue: ,
		}
		// Look for and delete any exiting patype.PA_ENC_TIMESTAMP
		for ,  := range .PAData {
			if .PADataType == patype.PA_ENC_TIMESTAMP {
				.PAData[] = .PAData[len(.PAData)-1]
				.PAData = .PAData[:len(.PAData)-1]
			}
		}
		.PAData = append(.PAData, )
	}
	return nil
}

// preAuthEType establishes what encryption type to use for pre-authentication from the KRBError returned from the KDC.
func preAuthEType( *messages.KRBError) ( etype.EType,  error) {
	//RFC 4120 5.2.7.5 covers the preference order of ETYPE-INFO2 and ETYPE-INFO.
	var  int32
	var  types.PADataSequence
	 := .Unmarshal(.EData)
	if  != nil {
		 = krberror.Errorf(, krberror.EncodingError, "error unmashalling KRBError data")
		return
	}
:
	for ,  := range  {
		switch .PADataType {
		case patype.PA_ETYPE_INFO2:
			,  := .GetETypeInfo2()
			if  != nil {
				 = krberror.Errorf(, krberror.EncodingError, "error unmashalling ETYPE-INFO2 data")
				return
			}
			 = [0].EType
			break 
		case patype.PA_ETYPE_INFO:
			,  := .GetETypeInfo()
			if  != nil {
				 = krberror.Errorf(, krberror.EncodingError, "error unmashalling ETYPE-INFO data")
				return
			}
			 = [0].EType
		}
	}
	,  = crypto.GetEtype()
	if  != nil {
		 = krberror.Errorf(, krberror.EncryptingError, "error creating etype")
		return
	}
	return , nil
}